package configutil

import (
	"fmt"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"sync"

	Log "github.com/cihub/seelog"
	"gopkg.in/ini.v1"
)

var (
	iniCfgMap   = map[string]*ini.File{}
	readIniLock sync.Mutex //读配置锁
)

// 读取配置文件
func readIni(iniPath string) (bool, string, interface{}) {
	if iniPath == "" {
		iniPath = "./config/app.ini"
	}

	readIniLock.Lock()
	defer readIniLock.Unlock()

	if result, ok := iniCfgMap[iniPath]; ok {
		return true, "已经读取成功", result
	}

	if strings.HasSuffix(iniPath, "./") {
		root := ""
		exePath, err := os.Executable()
		if err != nil {
			root = "."
		}

		root, _ = filepath.EvalSymlinks(filepath.Dir(exePath))
		iniPath = strings.Replace(iniPath, "./", root, -1)
	}

	if !fileExists(iniPath) {
		return false, "文件不存在", 1001
	}

	fp := getRootPath(iniPath)
	temp, err := ini.Load(fp)
	if err != nil {
		return false, "加载配置文件失败:", 1002
	}

	iniCfgMap[iniPath] = temp // 存储最终结果

	return true, "查询成功", temp
}

// 判断文件是否存在
func fileExists(filename string) bool {
	info, err := os.Stat(filename)
	if os.IsNotExist(err) {
		return false
	}
	return !info.IsDir()
}

/**
 * 读取配置文件中指定块下的指定键
 * @param filePath 配置文件路径
 * @param section 块名
 * @param key 键名
 * @param def 默认值
 * @return 按默认值类型返回
 */
func ReadConfigKey(iniPath, section, key string, def interface{}) interface{} {
	bl, msg, data := readIni(iniPath)
	if !bl {
		Log.Error(msg)
		return def
	}

	cfg := data.(*ini.File)
	keys, err := cfg.GetSection(section)
	if err != nil {
		return def
	}

	value, err := keys.GetKey(key)
	if err != nil {
		return def
	}

	if value == nil {
		return def
	}

	if def == nil {
		def = ""
	}

	isEncrypt := false
	temp := strings.TrimSpace(fmt.Sprintf("%v", value))
	if strings.HasPrefix(temp, "${") && strings.HasSuffix(temp, "}") {
		isEncrypt = true
		temp = Decrypt(temp[2:len(temp)-1], "bricks")
	}

	switch def.(type) {
	case string: // 将interface转为string字符串类型
		if isEncrypt {
			return temp
		}
		return value.String()
	case int:
		if isEncrypt {
			result, err := strconv.Atoi(temp)
			if err != nil {
				return def
			}
			return result
		}

		result, err := value.Int()
		if err != nil {
			return def
		}

		return result
	case int64:
		if isEncrypt {
			result, err := strconv.ParseInt(temp, 10, 64)
			if err != nil {
				return def
			}
			return result
		}

		result, err := value.Int64()
		if err != nil {
			return def
		}

		return result
	case float64:
		if isEncrypt {
			result, err := strconv.ParseFloat(temp, 64)
			if err != nil {
				return def
			}
			return result
		}

		result, err := value.Float64()
		if err != nil {
			return def
		}

		return result
	case bool:
		if isEncrypt {
			result, err := strconv.ParseBool(temp) //接受 1, t, T, TRUE, true, True, 0, f, F, FALSE, false, False 等字符串；
			if err != nil {
				return def
			}
			return result
		}

		result, err := value.Bool()
		if err != nil {
			return def
		}

		return result
	default:
		return value.String()
	}
}

// 取配置节点
func GetSection(iniPath, section string) (bool, string, interface{}) {
	bl, msg, data := readIni(iniPath)
	if !bl {
		return false, msg, data
	}

	cfg := data.(*ini.File)
	keys, err := cfg.GetSection(section)
	if err != nil {
		return false, err.Error(), data
	}

	return true, "读取成功", keys
}

// 删除配置缓存
func Remove(iniPath string) {
	delete(iniCfgMap, iniPath)
}

/**
 * 读取配置文件中指定块下的指定键
 * @param filePath 配置文件路径
 * @param section 块名
 * @param key 键名
 * @param def 默认值
 * @return 字符串
 */
func ReadString(filePath, section, key, def string) string {
	bl, _, data := readIni(filePath)
	if !bl {
		return def
	}

	cfg := data.(*ini.File)

	result, _ := cfg.Section(section).GetKey(key)
	if result == nil {
		return def
	}

	return result.String()
}

/**
 * 读取配置文件中指定块下的指定键
 * @param filePath 配置文件路径
 * @param section 块名
 * @param key 键名
 * @param def 默认值
 * @return 整型
 */
func ReadInt(filePath, section, key string, def int) int {
	bl, _, data := readIni(filePath)
	if !bl {
		return def
	}

	cfg := data.(*ini.File)

	val, _ := cfg.Section(section).GetKey(key)
	if val == nil {
		return def
	}

	result, _ := val.Int()

	return result
}

/**
 * 读取配置文件中指定块下的指定键
 * @param filePath 配置文件路径
 * @param section 块名
 * @param key 键名
 * @param def 默认值
 * @return 整型64
 */
func ReadInt64(filePath, section, key string, def int64) int64 {
	bl, _, data := readIni(filePath)
	if !bl {
		return def
	}

	cfg := data.(*ini.File)

	val, _ := cfg.Section(section).GetKey(key)
	if val == nil {
		return def
	}

	result, _ := val.Int64()

	return result
}

/**
 * 读取配置文件中指定块下的指定键
 * @param filePath 配置文件路径
 * @param section 块名
 * @param key 键名
 * @param def 默认值
 * @return 浮点型64
 */
func ReadFloat64(filePath, section, key string, def float64) float64 {
	bl, _, data := readIni(filePath)
	if !bl {
		return def
	}

	cfg := data.(*ini.File)

	val, _ := cfg.Section(section).GetKey(key)
	if val == nil {
		return def
	}

	result, _ := val.Float64()

	return result
}

/**
 * 读取配置文件中指定块下的指定键
 * @param filePath 配置文件路径
 * @param section 块名
 * @param key 键名
 * @param def 默认值
 * @return 布尔值
 */
func ReadBool(filePath, section, key string, def bool) bool {
	bl, _, data := readIni(filePath)
	if !bl {
		return def
	}

	cfg := data.(*ini.File)

	val, _ := cfg.Section(section).GetKey(key)
	if val == nil {
		return def
	}

	result, _ := val.Bool()

	return result
}

/**
 * 读取配置文件中指定块下的指定键
 * @param filePath 配置文件路径
 * @param section 块名
 * @param key 键名
 * @param def 默认值
 * @return interface{}
 */
func ReadKey(filePath, section, key string, def interface{}) interface{} {
	bl, _, data := readIni(filePath)
	if !bl {
		return def
	}

	cfg := data.(*ini.File)

	title := cfg.Section(section)
	value, _ := title.GetKey(key)
	if value == nil {
		return def
	}

	if def == nil {
		def = ""
	}

	isEncrypt := false
	temp := fmt.Sprintf("%v", value)
	if strings.HasPrefix(temp, "${") && strings.HasSuffix(temp, "}") {
		isEncrypt = true
		temp = Decrypt(temp[2:len(temp)-1], "bricks")
	}

	switch def.(type) {
	case string: // 将interface转为string字符串类型
		if isEncrypt {
			return temp
		}
		return value.String()
	case int:
		if isEncrypt {
			result, err := strconv.Atoi(temp)
			if err != nil {
				return def
			}
			return result
		}

		result, err := value.Int()
		if err != nil {
			return def
		}

		return result
	case int64:
		if isEncrypt {
			result, err := strconv.ParseInt(temp, 10, 64)
			if err != nil {
				return def
			}
			return result
		}

		result, err := value.Int64()
		if err != nil {
			return def
		}

		return result
	case float64:
		if isEncrypt {
			result, err := strconv.ParseFloat(temp, 64)
			if err != nil {
				return def
			}
			return result
		}

		result, err := value.Float64()
		if err != nil {
			return def
		}

		return result
	case bool:
		if isEncrypt {
			result, err := strconv.ParseBool(temp) //接受 1, t, T, TRUE, true, True, 0, f, F, FALSE, false, False 等字符串；
			if err != nil {
				return def
			}
			return result
		}

		result, err := value.Bool()
		if err != nil {
			return def
		}

		return result
	default:
		return value.String()
	}
}

/**
 * 获取配置文件绝对路径
 * @param filePath 配置文件路径
 * @return 绝对路径
 */
func getRootPath(filePath string) string {
	result := strings.TrimSpace(filePath)
	if result == "" {
		return ""
	}

	if strings.HasPrefix(result, "./") {
		exePath, _ := os.Executable()
		exePath = filepath.Dir(exePath)
		result = exePath + result[1:]
	}

	return result
}
